package suncertify.view.client;

import suncertify.service.Service;
import suncertify.service.ServiceEvent;
import suncertify.view.TableAdapter;
import suncertify.util.Notification;

import javax.swing.*;
import javax.swing.table.TableModel;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
import java.util.Observer;
import java.util.Observable;
import java.util.Comparator;
import java.awt.*;
import java.awt.event.ComponentListener;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;

/**
 * DBTable represents Table UI for data service
 */
public class DBTable extends JTable implements Observer {
    private static final int MAX_COLUMN_WIDTH = 500;
    private static final int COLUMN_HEADER_INSET = 20;
    private static final int COLUMN_DATA_INSET = 10;
    private static final int COMPUTE_COLUMN = 10;
    private TableAdapter adapter;
    private Service service;

    /**
     * constructor
     * @param service data service
     */
    public DBTable(Service service) {
        super();
        this.setDoubleBuffered(true);
        this.service = service;
        this.service.addObserver(this);
        this.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    }

    private void init() {
        this.setModel(adapter);
        this.resizeColumns();
        this.setVisible(true);
    }


    /**
     * updates the current view componen state
     * @param observable event source
     * @param o event object sent to observers
     */
    public void update(Observable observable, Object o) {
        if (o == null || !(o instanceof Notification)) {
            return;
        }
        final Notification n = (Notification) o;
        final DBTable table = this;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                switch ((ServiceEvent) n.getEvent()) {
                    case FOUND:
                        refresh();
                        break;
                    case DELETED:
                        int index = getSelectedIndex();
                        adapter.fireTableRowsDeleted(index, index);
                        break;
                    case INIT_ERROR:
                        table.setVisible(false);
                        break;
                    case INITED:
                        adapter = new TableAdapter(service);
                        init();
                        break;
                    case LOCKED:
                    case UPDATED:
                        index = getSelectedIndex();
                        adapter.fireTableRowsUpdated(index, index);
                        break;
                    case CREATED:
                        index = service.getRowCount() - 1;
                        adapter.fireTableRowsInserted(index, index);
                        break;
                    case DATA_CHANGED:
                        index = getSelectedIndex();
                        adapter.fireTableDataChanged();
                        if (index >= 0) {
                            if (index >= adapter.getRowCount()) {
                                index = adapter.getColumnCount() - 1;
                            }
                            setSelectedIndex(index);
                        }
                        break;
                    case ROW_REMOVED:
                        adapter.fireTableRowsDeleted((Integer) n.getData(), (Integer) n.getData());
                        break;
                }
            }
        });
    }

    protected void setSelectedIndex(int index) {
        getSelectionModel().setSelectionInterval(index, index);
    }

    protected int getSelectedIndex() {
        return getSelectionModel().getMinSelectionIndex();
    }

    /**
     * refresh data from service and resize columns for new data
     */
    public void refresh() {
        resizeColumns();
        adapter.fireTableDataChanged();
    }


    private void resizeColumns() {
        FontMetrics fm = this.getFontMetrics(this.getFont());
        TableModel ta = this.getModel();
        TableColumnModel cmodel = this.getColumnModel();
        for (int i = 0; i < ta.getColumnCount(); i++) {
            TableColumn col = cmodel.getColumn(i);
            String name = ta.getColumnName(i);
            int sWidth = fm.stringWidth(name);
            if (col.getWidth() - COLUMN_HEADER_INSET < sWidth && sWidth < MAX_COLUMN_WIDTH) {
                col.setPreferredWidth(sWidth + COLUMN_HEADER_INSET);
                col.setWidth(sWidth + COLUMN_HEADER_INSET);
            }
        }

        for (int i = 0; i < Math.min(COMPUTE_COLUMN, ta.getRowCount()); i++) {
            for (int j = 0; j < ta.getColumnCount(); j++) {
                TableColumn col = cmodel.getColumn(j);
                String data = ta.getValueAt(i, j).toString();
                if (data != null) {
                    int sWidth = fm.stringWidth(data);
                    if (col.getWidth() - COLUMN_DATA_INSET < sWidth && sWidth < MAX_COLUMN_WIDTH) {
                        col.setPreferredWidth(sWidth + COLUMN_DATA_INSET);
                        col.setWidth(sWidth + COLUMN_DATA_INSET);
                    }
                }
            }
        }
    }
}
